package io.light.frame.dal.mybatis.generator.core.context;

import io.light.frame.dal.mybatis.generator.core.exceptions.MybatisGenException;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.concurrent.NotThreadSafe;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Error stack
 *
 * @author Ivan
 * @version 1.0.0
 * @date 2021-06-14 12:54
 */
@NotThreadSafe
public class ErrorStack extends LinkedList<ErrorStack.Item> {

    private static final int DEFAULT_MAX = 32;

    private final int max;

    public ErrorStack() {
        this(DEFAULT_MAX);
    }

    public ErrorStack(int max) {
        this.max = Math.max(max, DEFAULT_MAX);
    }

    private void check() {
        if (size() >= max) {
            throw new MybatisGenException("Error stack overflow");
        }
    }

    public String getMessage() {
        if (isEmpty()) {
            return null;
        } else {
            return stream().filter(Objects::nonNull).map(ErrorStack.Item::getMsg)
                    .filter(StringUtils::isNotBlank).collect(Collectors.joining(";"));
        }
    }

    @Override
    public void addFirst(ErrorStack.Item e) {
        check();
        super.addFirst(e);
    }

    @Override
    public void addLast(ErrorStack.Item e) {
        check();
        super.addLast(e);
    }

    @Override
    public boolean add(ErrorStack.Item e) {
        push(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends ErrorStack.Item> c) {
        check();
        return super.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection<? extends ErrorStack.Item> c) {
        check();
        return super.addAll(index, c);
    }

    @Override
    public ErrorStack.Item set(int index, ErrorStack.Item element) {
        check();
        return super.set(index, element);
    }

    @Override
    public void add(int index, ErrorStack.Item element) {
        check();
        super.add(index, element);
    }

    @Override
    public boolean offer(ErrorStack.Item e) {
        check();
        return super.offer(e);
    }

    @Override
    public void push(ErrorStack.Item e) {
        addFirst(e);
    }

    @Getter
    @Setter
    public static class Item implements Serializable {
        private String msg;
        private transient Exception err;

        public static Item of(Exception err) {
            return of(null, err);
        }

        public static Item of(String msg, Exception err) {
            Item item = new Item();
            item.setMsg(StringUtils.isBlank(msg) ? err.getMessage() : msg);
            item.setErr(err);
            return item;
        }
    }
}
